package com.kadron.provider;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.kadron.common.KUtil;
import com.kadron.common.KeyValue;
import com.kadron.dao.KDiscountItemDAO;
import com.kadron.datamodel.KDataProvider;
import com.kadron.datamodel.KModel;
import com.kadron.datamodel.KModelCreator;
import com.kadron.entity.Embed;
import com.kadron.item.KItemListSearch;

public class KDiscountItemProvider implements KDataProvider {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5862117516679924629L;
	
	private KModelCreator creator;
	private KDiscountItemDAO discountItemDAO;
	private KItemListSearch search;
	private long discountId;
	
	public KDiscountItemProvider(KModelCreator creator,KItemListSearch search, long discountId, KDiscountItemDAO discountItemDAO) {
		super();
		this.creator = creator;
		this.search = search;
		this.discountId = discountId;
		this.discountItemDAO = discountItemDAO;
	}

	@SuppressWarnings("unchecked")
	public List<Long> getItemsByRange(Integer first, int numberOfRows,
			Object object, boolean b) {
		Query query = createQuery(false);
		query.setFirstResult(first);
		query.setMaxResults(numberOfRows);		
		return query.getResultList();
	}

	@Override
	public KModel getItemById(Long id, KModelCreator creator) {
		return getCreator().create(id);
	}

	@Override
	public int getRowCount() {
		Query query = createQuery(true);
		Long countNumber = (Long) query.getSingleResult();
		return (countNumber == null) ? 0 : countNumber.intValue();
	}

	public boolean hasItemWithId(Long id) {
		return discountItemDAO.exists(id, Embed.class);
	}

	public KModelCreator getCreator() {
		return creator;
	}
	
	public KItemListSearch getSearch() {
		return search;
	}

	public void setSearch(KItemListSearch search) {
		this.search = search;
	}
	
	private final Query createQuery(boolean isCount) {
		EntityManager em = discountItemDAO.getEntityManager();
		boolean currency_ = search.getCurrencyId() != null;
		boolean price_ = (search.getPriceFrom() != null || search.getPriceTo() != null);
		Set<KeyValue> params = new HashSet<KeyValue>();
		StringBuilder builder = new StringBuilder("SELECT ");
		if (isCount) {			
			builder.append("COUNT(a.id) ");
		} else {			
			builder.append(" a.id ");
		}
		builder.append("FROM Item a WHERE a.active=TRUE AND a.validFrom <= :moment AND a.validTo >= :moment ");
		builder.append("AND NOT EXISTS(SELECT 1 FROM DiscountItem ai WHERE ai.discount.id = :discount_id and a.id = ai.item.id) ");
		params.add(new KeyValue("discount_id", discountId));
		params.add(new KeyValue("moment", KUtil.getMoment()));
		if (search.getValidTo()!= null) {
			builder.append(" AND a.validTo >= :validtTo ");
			params.add(new KeyValue("validtTo", search.getValidTo()));
		}
		if (currency_ && !price_) {
			builder.append(" AND EXISTS(SELECT 1 FROM Price p WHERE p.item.id=a.id AND p.currency.id = :currency_id AND p.price > 0) ");
			params.add(new KeyValue("currency_id", search.getCurrencyId()));
		} else {
			builder.append(" AND EXISTS(SELECT 1 FROM Price p WHERE p.item.id=a.id  ");
			if (currency_) {
				builder.append(" AND p.currency.id = :currency_id ");				
				params.add(new KeyValue("currency_id", search.getCurrencyId()));
			}
			if (search.getPriceFrom()!= null) {
				builder.append(" AND p.price >= :priceFrom ");				
				params.add(new KeyValue("priceFrom", search.getPriceFrom()));
			}
			if (search.getPriceTo()!= null) {
				builder.append(" AND p.price <= :priceTo ");								
				params.add(new KeyValue("priceTo", search.getPriceTo()));
			}
			builder.append(")");								
		}
		if (search.isAdvanced()) {
			if (!search.getColors().isEmpty()) {
				builder.append(" AND EXISTS(SELECT 1 FROM ItemColor ic INNER JOIN ic.color itc INNER JOIN itc.color c WHERE a.id = ic.item.id AND c IN (:colors)) ");								
				params.add(new KeyValue("colors", search.getColors()));				
			}
			if (!search.getSizes().isEmpty()) {
				builder.append(" AND EXISTS(SELECT 1 FROM ItemSize isz INNER JOIN isz.size its INNER JOIN its.size s WHERE a.id = isz.item.id AND s IN (:sizes)) ");								
				params.add(new KeyValue("sizes", search.getSizes()));				
			}
			if (!search.getMaterials().isEmpty()) {
				builder.append(" AND EXISTS(SELECT 1 FROM ItemMaterial im INNER JOIN im.material itm INNER JOIN itm.material m WHERE a.id = im.item.id AND m IN (:materials)) ");								
				params.add(new KeyValue("materials", search.getMaterials()));				
			}
		} else {
			if (search.getColor() != null) {
				builder.append(" AND EXISTS(SELECT 1 FROM ItemColor ic INNER JOIN ic.color itc INNER JOIN itc.color c WHERE a.id = ic.item.id AND c.id = :color_id) ");								
				params.add(new KeyValue("color_id", search.getColor().getId()));				
			}
			if (search.getSize() != null) {
				builder.append(" AND EXISTS(SELECT 1 FROM ItemSize isz INNER JOIN isz.size its INNER JOIN its.size s WHERE a.id = isz.item.id AND s.id = :size_id) ");								
				params.add(new KeyValue("size_id", search.getSize().getId()));				
			}
			if (search.getMaterial() != null) {
				builder.append(" AND EXISTS(SELECT 1 FROM ItemMaterial im INNER JOIN im.material itm INNER JOIN itm.material m WHERE a.id = im.item.id AND m.id = :material_id) ");								
				params.add(new KeyValue("material_id", search.getMaterial().getId()));				
			}			
		}
		if (!isCount) {
			builder.append("ORDER BY a.id DESC");
		}
		Query query = em.createQuery(builder.toString());
		for (KeyValue p : params) {
			query.setParameter(p.getKey(), p.getValue());
		}
		return query;
	}
}
